<?php
/**
 * Created by PhpStorm.
 * User: Zhangxy
 * Date: 2018/8/17
 * Time: 10:22
 */

namespace App\Libraries;

use Carbon\Carbon;
use Illuminate\Support\Facades\Cache;

/**
 * 文件解析器
 * 主要是生成权限和路由相关的一些方法汇总
 * Class FileParser
 * @package App\Knowledge
 */
class FileParser
{

    protected $modulePath = 'Http/Controllers/Admin';
    protected $exceptMethods = ['getMiddleware', 'getRouter'];
    protected $isMenuMethods = ['getIndex', 'getList'];
    public $ckModuleInfo = 'ck_module_info';

    /**
     * FileParser constructor.
     * @param $exceptMethods
     */
    public function __construct(array $exceptMethods = [])
    {
        if (!is_empty($exceptMethods)) {
            $this->setExceptMethods($exceptMethods);
        }
    }

    /**
     * @param mixed $exceptMethods
     */
    public function setExceptMethods($exceptMethods)
    {
        $this->exceptMethods = $exceptMethods;
    }

    /**
     * 通过解析模块配置文件获取某路径下的模块信息
     * @param string $name 模块名称
     * @param string $path 模块所属路径
     * User: Zhangxy
     * Date: 2018-08-17
     * @return array
     */
    public function getModuleInfo($name = null, $path = null)
    {
        if (empty($path)) {
            $path = app_path($this->modulePath);
        }
        if (!empty($name)) {
            $modulePath = $this->getModuleDirName($name, $path);
        } else {
            $modulePath = $path;
        }

        $moduleList = [];
        if (is_dir($modulePath)) {
            $moduleFiles = getFiles($modulePath, true, '.module');
            if (!empty($moduleFiles)) {
                foreach ($moduleFiles as $index => $moduleFile) {
                    $moduleInfo = parse_ini_file($moduleFile);
                    $moduleInfo['filename'] = $moduleFile;
                    $moduleList[$moduleInfo['name']] = $moduleInfo;
                }

                if (empty($name)) {
                    $expiresAt = Carbon::now()->addDay(1);
                    Cache::put($this->ckModuleInfo, $moduleList, $expiresAt);
                }
            }
        }
        if (!empty($name)) {
            return current($moduleList);
        }
        return $moduleList;
    }

    /**
     * 根据模块儿的名称获取到模块的路径
     * @param $name
     * @param null $path
     * User: Zhangxy
     * Date: 2018-09-17
     * @return string
     */
    protected function getModuleDirName($name, $path = null)
    {

        $dirName = '';
        if (empty($path)) {
            $path = app_path($this->modulePath);
        }

        $formatName = camel_case(str_replace('-', '_', $name));
        $d = dir($path);
        while (false !== ($file = $d->read())) {
            if ($file != '.' && $file != '..') {
                if (is_dir($path . '/' . $file)) {
                    if (strtolower($formatName) == strtolower($file)) {
                        $dirName = $path . '/' . $file;
                        break;
                    }
                }
            }
        }
        return $dirName;
    }

    /**
     * 获取 controller 的所有文档信息
     * @param $moduleName
     * @param bool $withMethod
     * User: Zhangxy
     * Date: 2018-08-17
     * @return array
     */
    public function getControllersInfo($moduleName, $withMethod = true)
    {

        $controllersInfo = [];
        $moduleList = Cache::get($this->ckModuleInfo);
        if (empty($moduleList)) {
            $moduleList = $this->getModuleInfo();
        }

        if (isset($moduleList[$moduleName])) {
            $moduleInfo = $moduleList[$moduleName];
            $modulePath = dirname($moduleInfo['filename']);
            $controllerFiles = getFiles($modulePath, true, 'Controller.php');

            $classNames = $this->getClassNames($controllerFiles);

            if (!empty($classNames)) {
                foreach ($classNames as $index => $className) {
                    $controller = new \ReflectionClass($className);
                    $controllerInfo = $this->getControllerInfo($controller);
                    if (empty($controllerInfo['module'])) {
                        $controllerInfo['module'] = $moduleInfo['name'];
                        $controllerInfo['full_name'] = sprintf("%s.%s", $moduleInfo['name'], $controllerInfo['name']);
                    } else {
                        $controllerInfo['full_name'] = sprintf("%s.%s", $controllerInfo['module'],
                            $controllerInfo['name']);
                    }

                    if (true == $withMethod) {
                        $controllerInfo['methods'] = $this->getMethodsInfo($controller);
                        if (isset($controllerInfo['methods']) && !empty($controllerInfo['methods'])) {
                            foreach ($controllerInfo['methods'] as $index => &$method) {
                                $method['full_name'] = sprintf("%s.%s", $controllerInfo['full_name'], $method['name']);
                            }
                            unset($method);
                        }
                    }

                    $controllersInfo[$controllerInfo['full_name']] = $controllerInfo;
                }
            }
        }
        return $controllersInfo;
    }

    /**
     * 获取单个 controller 的所有文档信息
     * @param $controller
     * User: Zhangxy
     * Date: 2018-08-17
     * @return array
     */
    public function getControllerInfo($controller)
    {
        $className = $controller->getShortName();
        $controllerName = str_replace('_', '-', strtolower(snake_case(substr($className, 0, -10))));
        $doc = $controller->getDocComment();
        // Controller 默认为菜单
        return $this->getCommentInfo($doc, $controllerName, 0);
    }


    /**
     * 获取所有方法的权限信息
     * @param $controller
     * User: Zhangxy
     * Date: 2018-08-17
     * @return array
     */
    public function getMethodsInfo($controller)
    {
        $methodsInfo = [];
        $methods = $controller->getMethods(\ReflectionMethod::IS_PUBLIC);

        if (!empty($methods)) {
            $routeMethods = $this->getRouteMethods($methods);
            if (!empty($routeMethods)) {
                foreach ($routeMethods as $index => $method) {
                    // getUpdate 方法特殊处理
                    if ('getUpdate' == $method->name) {

                        $methodInfo = $this->getMethodInfo($method);
                        $methodInfo['title'] = '编辑';
                        $methodsInfo[] = $methodInfo;
                        $methodInfo['name'] = 'create';
                        $methodInfo['title'] = '添加';
                        $methodsInfo[] = $methodInfo;

                    } else {
                        $methodsInfo[] = $this->getMethodInfo($method);
                    }
                }
            }
        }

        return $methodsInfo;
    }


    /**
     * 获取单个方法的权限信息
     * @param $method
     * User: Zhangxy
     * Date: 2018-08-17
     * @return array
     */
    public function getMethodInfo($method)
    {
        $snakeName = snake_case($method->name);
        $names = explode('_', $snakeName);
        array_shift($names);
        $routeName = implode('-', $names);
        $doc = $method->getDocComment();
        $isMenu = in_array($method->name, $this->isMenuMethods) ? 1 : 0;
        $methodsInfo = $this->getCommentInfo($doc, $routeName, $isMenu);

        return $methodsInfo;

    }


    /**
     * 获取需要被路由的方法
     * @param $methods
     * User: Zhangxy
     * Date: 2018-08-17
     * @return array
     */
    protected function getRouteMethods($methods)
    {
        $routeMethods = [];
        if (!empty($methods)) {
            foreach ($methods as $index => $method) {
                $prefix = strtolower(substr($method->name, 0, 3));
                if (in_array($prefix, ['get', 'pos', 'put', 'del', 'hea', 'opt']) && !in_array($method->name,
                        $this->exceptMethods)
                ) {
                    $routeMethods[] = $method;
                }
            }
        }

        return $routeMethods;
    }

    /**
     * 获取注释信息
     * @param $doc
     * @param string $defaultName 默认名称(类或方法)
     * @param int $is_menu 是否为菜单
     * User: Zhangxy
     * Date: 2018-08-17
     * @return array
     */
    public function getCommentInfo($doc, $defaultName = '', $is_menu = 0)
    {
        $commentInfo = [];
        $fields =
            [
                'module' => '',
                'name' => $defaultName,
                'title' => $defaultName,
                'is_menu' => $is_menu,
                'position' => 0
            ];

        foreach ($fields as $field => $value) {
            $commentInfo[$field] = $this->parseComment($doc, $field, $value);
        }
        return $commentInfo;
    }

    /**
     * 解析 注释信息
     * @param $doc
     * @param string $param
     * @param string $defaultValue
     * User: Zhangxy
     * Date: 2018-08-17
     * @return string
     */
    protected function parseComment($doc, $param = 'name', $defaultValue = null)
    {
        $commentPattern = '/\@' . $param . '\s(\S*)\s/';
        preg_match($commentPattern, $doc, $comments);
        $cdName = '';
        if (!empty($comments) && isset($comments[1])) {
            $cdName = $comments[1];
        }
        return (is_null($cdName) || '' == $cdName) ? $defaultValue : $cdName;
    }


    /**
     * 根据文件获取类名 批量
     * @param $files
     * User: Zhangxy
     * Date: 2018-08-17
     * @return array|null
     */
    public function getClassNames($files)
    {
        $classNames = [];
        if (is_array($files)) {
            foreach ($files as $index => $file) {
                $classNames[] = $this->getClassName($file);
            }
        }
        return $classNames;
    }

    /**
     * 根据文件获取类名
     * @param $file
     * User: Zhangxy
     * Date: 2018-08-17
     * @return string
     */
    public function getClassName($file)
    {
        $className = '';
        if (false !== stripos($file, app_path())) {
            $className = str_replace('/', '\\', 'App' . substr($file, strlen(app_path()), -4));
        }
        return $className;
    }


}